package org.aphelie.torg.ruler.internal.core.character;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import org.aphelie.torg.ruler.core.IModelChangedEvent;
import org.aphelie.torg.ruler.core.character.ICharacterBase;
import org.aphelie.torg.ruler.core.character.ICharacterExtension;
import org.aphelie.torg.ruler.core.character.ICharacterExtensionPoint;
import org.aphelie.torg.ruler.core.character.ICharacterObject;
import org.aphelie.torg.ruler.core.character.IExtensions;
import org.aphelie.torg.ruler.core.character.ISharedCharacterModel;
import org.aphelie.torg.ruler.internal.core.TGRCore;
import org.aphelie.torg.ruler.internal.core.TGRCoreMessages;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;


public abstract class AbstractExtensions extends CharacterObject implements IExtensions {

    private static final long serialVersionUID = 1L;

    protected String fSchemaVersion;

    protected List fExtensions = null;
    protected List fExtensionPoints = null;
    boolean fCache = false;

    public AbstractExtensions(boolean readOnly) {
        fCache = !readOnly;
    }

    public void add(ICharacterExtension extension) throws CoreException {
        ensureModelEditable();
        getExtensionsList().add(extension);
        ((CharacterExtension) extension).setInTheModel(true);
        ((CharacterExtension) extension).setParent(this);
        fireStructureChanged(extension, IModelChangedEvent.INSERT);
    }

    /* TODO
    public void add(ICharacterExtensionPoint extensionPoint) throws CoreException {
        ensureModelEditable();
        getExtensionPointsList().add(extensionPoint);
        ((CharacterExtensionPoint) extensionPoint).setInTheModel(true);
        ((CharacterExtensionPoint) extensionPoint).setParent(this);
        fireStructureChanged(extensionPoint, IModelChangedEvent.INSERT);
    }
    */

    public ICharacterExtensionPoint[] getExtensionPoints() {
        List extPoints = getExtensionPointsList();
        return (ICharacterExtensionPoint[]) extPoints.toArray(new ICharacterExtensionPoint[extPoints.size()]);
    }

    public ICharacterExtension[] getExtensions() {
        List extensions = getExtensionsList();
        return (ICharacterExtension[]) extensions.toArray(new ICharacterExtension[extensions.size()]);
    }

    public void restoreProperty(String name, Object oldValue, Object newValue) throws CoreException {
        if (name.equals(P_EXTENSION_ORDER)) {
            swap((ICharacterExtension) oldValue, (ICharacterExtension) newValue);
            return;
        }
        super.restoreProperty(name, oldValue, newValue);
    }

    public void load(IExtensions srcExtensions) {
        addArrayToVector(getExtensionsList(), srcExtensions.getExtensions());
        //TODO addArrayToVector(getExtensionPointsList(), srcExtensions.getExtensionPoints());
    }

    protected void addArrayToVector(List vector, Object[] array) {
        for (int i = 0; i < array.length; i++) {
            Object obj = array[i];
            if (obj instanceof CharacterObject)
                ((CharacterObject) obj).setParent(this);
            vector.add(obj);
        }
    }

    public void remove(ICharacterExtension extension) throws CoreException {
        ensureModelEditable();
        getExtensionsList().remove(extension);
        ((CharacterExtension) extension).setInTheModel(false);
        fireStructureChanged(extension, IModelChangedEvent.REMOVE);
    }

    /* TODO
    public void remove(ICharacterExtensionPoint extensionPoint) throws CoreException {
        ensureModelEditable();
        getExtensionPointsList().remove(extensionPoint);
        ((CharacterExtensionPoint) extensionPoint).setInTheModel(false);
        fireStructureChanged(extensionPoint, IModelChangedEvent.REMOVE);
    }
    */

    public void reset() {
        resetExtensions();
    }

    public void resetExtensions() {
        fExtensions = null;
        fExtensionPoints = null;
    }

    public int getExtensionCount() {
        return getExtensionsList().size();
    }

    public int getIndexOf(ICharacterExtension e) {
        return getExtensionsList().indexOf(e);
    }

    public void swap(ICharacterExtension e1, ICharacterExtension e2) throws CoreException {
        ensureModelEditable();
        List extensions = getExtensionsList();
        int index1 = extensions.indexOf(e1);
        int index2 = extensions.indexOf(e2);
        if (index1 == -1 || index2 == -1)
            throwCoreException(TGRCoreMessages.AbstractExtensions_extensionsNotFoundException);
        extensions.set(index2, e1);
        extensions.set(index2, e2);
        firePropertyChanged(this, P_EXTENSION_ORDER, e1, e2);
    }
    
    protected void writeChild(String indent, Object child, PrintWriter writer) {
        ICharacterObject obj = (ICharacterObject) child;
        obj.write(indent + "   ", writer); //$NON-NLS-1$
    }

    protected void writeChildren(String indent, String tag, Object[] children, PrintWriter writer) {
        writer.println(indent + "<" + tag + ">"); //$NON-NLS-1$ //$NON-NLS-2$
        for (int i = 0; i < children.length; i++) {
            ICharacterObject obj = (ICharacterObject) children[i];
            obj.write(indent + "   ", writer); //$NON-NLS-1$
        }
        writer.println(indent + "</" + tag + ">"); //$NON-NLS-1$ //$NON-NLS-2$
    }

    protected boolean hasRequiredAttributes() {
        // validate extensions
        List extensions = getExtensionsList();
        int size = extensions.size();
        for (int i = 0; i < size; i++) {
            ICharacterExtension extension = (ICharacterExtension) extensions.get(i);
            if (!extension.isValid())
                return false;
        }
        // validate extension points
        List extPoints = getExtensionPointsList();
        size = extPoints.size();
        for (int i = 0; i < size; i++) {
            ICharacterExtensionPoint expoint = (ICharacterExtensionPoint) extPoints.get(i);
            if (!expoint.isValid())
                return false;
        }
        return true;
    }

    public String getSchemaVersion() {
        if (fSchemaVersion == null) {
            // since schema version is only needed on workspace models in very few situations, reading information from the file should suffice
            ISharedCharacterModel model = getModel();
            if (model != null) {
                org.eclipse.core.resources.IResource res = model.getUnderlyingResource();
                if (res != null && res instanceof IFile) {
                    try {
                        InputStream stream = new BufferedInputStream(((IFile) res).getContents(true));
                        CharacterHandler handler = new CharacterHandler(true);
                        SAXParserFactory.newInstance().newSAXParser().parse(stream, handler);
                        return handler.getSchemaVersion();
                    } catch (CoreException e) {
                    } catch (SAXException e) {
                    } catch (IOException e) {
                    } catch (ParserConfigurationException e) {
                    }
                }
            }
        }
        return fSchemaVersion;
    }

    public void setSchemaVersion(String schemaVersion) throws CoreException {
        ensureModelEditable();
        String oldValue = fSchemaVersion;
        fSchemaVersion = schemaVersion;
        firePropertyChanged(ICharacterBase.P_SCHEMA_VERSION, oldValue, schemaVersion);
    }

    protected List getExtensionsList() {
        if (fExtensions == null) {
            ICharacterBase base = getCharacterBase();
            /* TODO
            if (base != null) {
                System.err.println("fExtensions = new ArrayList(Arrays.asList(TGRCore.getDefault().getExtensionsRegistry().findExtensionsForCharacter(base.getCharacterModel())));");

                if (fCache)
                    fExtensions = new ArrayList(Arrays.asList(TGRCore.getDefault().getExtensionsRegistry().findExtensionsForCharacter(base.getCharacterModel())));
                else
                    return Arrays.asList(TGRCore.getDefault().getExtensionsRegistry().findExtensionsForCharacter(base.getCharacterModel()));
                   
            } else { */
                //return Collections.EMPTY_LIST;
            return new ArrayList<IExtensions>();
            //}
        }
        return fExtensions;
    }

    protected List getExtensionPointsList() {
        if (fExtensionPoints == null) {
            ICharacterBase base = getCharacterBase();
            if (base != null) {
                System.err.println("fExtensionPoints = new ArrayList(Arrays.asList(TGRCore.getDefault().getExtensionsRegistry().findExtensionPointsForCharacter(base.getCharacterModel())));");
                /* TODO
                if (fCache)
                    fExtensionPoints = new ArrayList(Arrays.asList(TGRCore.getDefault().getExtensionsRegistry().findExtensionPointsForCharacter(base.getCharacterModel())));
                else
                    return Arrays.asList(TGRCore.getDefault().getExtensionsRegistry().findExtensionPointsForCharacter(base.getCharacterModel()));
                    */
            } else {
                return Collections.EMPTY_LIST;
            }
        }
        return fExtensionPoints;
    }

    /*
     * If this function is used to load the model, the extension registry cache will not be used when querying model.
     */
    protected void processChild(Node child) {
        String name = child.getNodeName();
        if (fExtensions == null)
            fExtensions = new ArrayList();
        if (fExtensionPoints == null)
            fExtensionPoints = new ArrayList();

        if (name.equals("extension")) { //$NON-NLS-1$
            CharacterExtension extension = new CharacterExtension();
            extension.setModel(getModel());
            extension.setParent(this);
            fExtensions.add(extension);
            extension.setInTheModel(true);
            extension.load(child);
        } /* TODO else if (name.equals("extension-point")) { //$NON-NLS-1$
            CharacterExtensionPoint point = new CharacterExtensionPoint();
            point.setModel(getModel());
            point.setParent(this);
            point.setInTheModel(true);
            fExtensionPoints.add(point);
            point.load(child);
        }*/
    }
}
